import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from keras.models import Model
from keras.layers import Input, LSTM, Dense, Dropout, Conv1D, MaxPooling1D, Flatten, concatenate, Attention
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from keras.callbacks import ReduceLROnPlateau, EarlyStopping, ModelCheckpoint
from keras.optimizers import Adam
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import ta
from openai import OpenAI
import os
import base64
import threading
import itertools
import sys
import time
from PIL import Image, ImageTk



# 加载数据
dataframe = pd.read_csv('BA.csv')
df = dataframe[['Date', 'Close', 'Open', 'High', 'Low', 'Volume', 'Adj Close']]
df['Date'] = pd.to_datetime(df['Date'])
df = df.sort_values('Date')

# 调用大模型进行图像分析
def analyze_image_with_model(image_path, description):
    base64_image = encode_image(image_path)
    client = OpenAI(
        api_key="sk-17f01ba971d7424b82aabf981db7e091",
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    )
    completion = client.chat.completions.create(
        model="qwen-vl-max-latest",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
                    },
                    {"type": "text", "text": description},
                ],
            }
        ],
    )
    return completion.choices[0].message.content

# 图像编码函数
def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")

# 数据预处理
def preprocess_data(df):
    def convert_volume(volume):
        if isinstance(volume, str):
            if 'M' in volume:
                return float(volume.replace('M', '')) * 1e6
            elif 'K' in volume:
                return float(volume.replace('K', '')) * 1e3
        return float(volume)

    df['Volume'] = df['Volume'].apply(convert_volume)


    def convert_percentage(value):
        if isinstance(value, str) and '%' in value:
            return float(value.replace('%', '')) / 100
        return float(value)

    df['Adj Close'] = df['Adj Close'].apply(convert_percentage)

    # 添加技术指标
    df['RSI'] = ta.momentum.RSIIndicator(df['Close'], window=14).rsi()
    df['MACD'] = ta.trend.MACD(df['Close']).macd_diff()
    bollinger = ta.volatility.BollingerBands(df['Close'], window=20, window_dev=2)
    df['Bollinger_High'] = bollinger.bollinger_hband()
    df['Bollinger_Low'] = bollinger.bollinger_lband()
    df['Volatility'] = df['Close'].rolling(window=10).std()
    # 填补缺失值
    df.fillna(method='bfill', inplace=True)

    # 数据标准化
    scaler = StandardScaler()
    scaled_features = scaler.fit_transform(df[['Close', 'Open', 'High', 'Low', 'Volume', 'Adj Close', 'RSI', 'MACD',
                                               'Bollinger_High', 'Bollinger_Low', 'Volatility']])
    df_scaled = pd.DataFrame(scaled_features,
                             columns=['Close', 'Open', 'High', 'Low', 'Volume', 'Adj Close', 'RSI', 'MACD',
                                      'Bollinger_High', 'Bollinger_Low', 'Volatility'])

    return df, df_scaled, scaler


df, df_scaled, scaler = preprocess_data(df)



# 创建滑动窗口
def create_sequences(data, seq_size):
    x_values, y_values = [], []
    for i in range(len(data) - seq_size):
        x_values.append(data[i:i + seq_size, :-1])
        y_values.append(data[i + seq_size, 0])
    return np.array(x_values), np.array(y_values)

seq_size = 30
future_steps = 10
x_data, y_data = create_sequences(df_scaled.values, seq_size)
# 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2, shuffle=False)

# 构建模型
def build_model(seq_size, feature_size, future_steps):
    input_layer = Input(shape=(seq_size, feature_size))

    # CNN 部分
    conv1 = Conv1D(filters=128, kernel_size=3, activation='relu', padding='same')(input_layer)
    conv1 = MaxPooling1D(pool_size=2)(conv1)
    conv2 = Conv1D(filters=256, kernel_size=3, activation='relu', padding='same')(conv1)
    conv2 = MaxPooling1D(pool_size=2)(conv2)
    cnn_output = Flatten()(conv2)

    # LSTM 部分
    lstm_output = LSTM(256, return_sequences=True)(input_layer)
    lstm_output = LSTM(128, return_sequences=False)(lstm_output)
    lstm_output = Dropout(0.3)(lstm_output)

    # 合并 CNN 和 LSTM 特征
    combined = concatenate([cnn_output, lstm_output])

    # 全连接层
    dense1 = Dense(256, activation='relu')(combined)
    dense1 = Dropout(0.3)(dense1)
    dense2 = Dense(128, activation='relu')(dense1)
    dense2 = Dropout(0.3)(dense2)

    # 输出层
    output = Dense(future_steps)(dense2)

    model = Model(inputs=input_layer, outputs=output)
    model.compile(optimizer=Adam(learning_rate=0.0001), loss='huber_loss')
    return model

model = build_model(seq_size, x_data.shape[2], future_steps)

# 添加学习率调度器和早停
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, min_lr=1e-6)
early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)
checkpoint = ModelCheckpoint('best_model.h5', monitor='val_loss', save_best_only=True, verbose=1)

# 训练模型
# model.fit(
#     x_train, y_train,
#     epochs=20,
#     batch_size=32,
#     validation_data=(x_test, y_test),
#     callbacks=[lr_scheduler, early_stopping, checkpoint],
#     verbose=1
# )
# model.save('trained_model_GE.h5')

# 载入最佳模型
model.load_weights('trained_model_BA.h5')


# 预测未来 10 天的收盘价格
last_sequence = x_data[-1].reshape(1, seq_size, x_data.shape[2])
future_predictions = model.predict(last_sequence).flatten()

# # 构造与标准化特征匹配的数组
future_predictions_extended = np.zeros((future_predictions.shape[0], x_data.shape[2]))
future_predictions_extended[:, 0] = future_predictions
# 使用 StandardScaler 反标准化
future_predictions_original = (future_predictions_extended[:, 0] * scaler.scale_[0]) + scaler.mean_[0]

# 风险检测
risk_threshold = 0.03
risks = []
for i in range(1, len(future_predictions_original)):
    change = (future_predictions_original[i] - future_predictions_original[i - 1]) / future_predictions_original[i - 1]
    if change < -risk_threshold:
        risks.append((i, future_predictions_original[i]))




##########################
##########################
# 创建 Tkinter 界面
##########################
##########################

root = tk.Tk()
root.title("金融风险评估与预警系统")
root.geometry("1400x800")

# # 使用 Notebook 创建选项卡
# notebook = ttk.Notebook(root)
# notebook.pack(fill='both', expand=True)
#
# # 选项卡1：金融风险评估与预警系统
# tab1 = ttk.Frame(notebook)
# notebook.add(tab1, text='金融风险评估与预警系统')
#
# # 选项卡2：股票市场数据分析
# tab2 = ttk.Frame(notebook)
# notebook.add(tab2, text='股票市场数据分析')
#
# # 选项卡3：预测与风险评估
# tab3 = ttk.Frame(notebook)
# notebook.add(tab3, text='股票市场预测与风险评估')
#
# # 选项卡4：未来市场分析
# tab4 = ttk.Frame(notebook)
# notebook.add(tab4, text='未来市场分析')
#
#
# ##########################
# # 选项卡1：系统封面
# ##########################
#
# # 图片路径
# cover_image_path = "fengmian.webp"  # 请替换为你的图片路径
#
# # 在 Canvas 上显示图片的函数
# def display_fullscreen_image(canvas, image_path):
#     """在 Canvas 上显示并适配窗口大小的图片"""
#     img = Image.open(image_path)
#     img_resized = img.resize((root.winfo_width(), root.winfo_height()), Image.LANCZOS)
#     photo_img = ImageTk.PhotoImage(img_resized)
#     canvas.image = photo_img  # 防止图片被垃圾回收
#     canvas.create_image(0, 0, anchor="nw", image=photo_img)
#
# # 创建 Canvas 并添加到 tab1
# canvas = tk.Canvas(tab1, highlightthickness=0)
# canvas.pack(fill="both", expand=True)
#
# # 初始化显示图片
# display_fullscreen_image(canvas, cover_image_path)
#
# # 窗口大小改变时动态调整图片
# def resize_image(event):
#     display_fullscreen_image(canvas, cover_image_path)
#
# root.bind("<Configure>", resize_image)  # 监听窗口大小变化

# 使用 Notebook 创建选项卡
notebook = ttk.Notebook(root)
notebook.pack(fill='both', expand=True)

# 选项卡1：金融风险评估与预警系统
tab1 = ttk.Frame(notebook)
notebook.add(tab1, text='金融风险评估与预警系统')

# 选项卡2：股票市场数据分析
tab2 = ttk.Frame(notebook)
notebook.add(tab2, text='股票市场数据分析')

# 选项卡3：股票市场预测与风险评估
tab3 = ttk.Frame(notebook)
notebook.add(tab3, text='股票市场预测与风险评估')

# 选项卡4：未来市场分析
tab4 = ttk.Frame(notebook)
notebook.add(tab4, text='未来市场分析')

##########################
# 选项卡1：系统封面
##########################

# 图片路径
cover_image_path = "fengmian.png"  # 替换为你的封面图片路径

# 在 Canvas 上显示图片的函数
def display_fullscreen_image(canvas, image_path):
    """在 Canvas 上显示并适配窗口大小的图片"""
    img = Image.open(image_path)
    img_resized = img.resize((root.winfo_width(), root.winfo_height()), Image.LANCZOS)
    photo_img = ImageTk.PhotoImage(img_resized)
    canvas.image = photo_img  # 防止图片被垃圾回收
    canvas.create_image(0, 0, anchor="nw", image=photo_img)

# 创建 Canvas 并添加到 tab1
canvas = tk.Canvas(tab1, highlightthickness=0)
canvas.pack(fill="both", expand=True)

# 初始化显示图片
display_fullscreen_image(canvas, cover_image_path)

# 窗口大小改变时动态调整图片
def resize_image(event):
    display_fullscreen_image(canvas, cover_image_path)

root.bind("<Configure>", resize_image)  # 监听窗口大小变化

# 添加 "下一页" 按钮
def go_to_next_tab():
    notebook.select(tab2)  # 切换到第二个选项卡

# 创建按钮并放置在封面 Canvas 上
next_button = tk.Button(tab1, text="下一页", command=go_to_next_tab, width=10, height=2,font=("Arial", 18))
next_button.place(relx=0.9, rely=0.9, anchor="center")  # 按钮位于页面右下角











##########################
# 选项卡2：市场数据分析
##########################

# 添加市场状态分析
frame_market_summary = ttk.LabelFrame(tab2, text="大模型数据分析-市场状态分析")
frame_market_summary.pack(fill="both", expand="yes", padx=10, pady=10)
# frame_market_summary = tk.LabelFrame(tab2, text="大模型数据分析-市场状态分析", bg="#f0f8ff", font=("Arial", 12, "bold"))
# frame_market_summary.pack(fill="both", expand="yes", padx=10, pady=10)

# 保存市场状态图像
market_status_image = "market_status_analysis.png"
# fig_market_status, ax_market_status = plt.subplots(figsize=(10, 5))
#
# # 绘制技术指标相关图表
# ax_market_status.plot(df['Date'], df['Close'], label='Close Price', color='blue')
# ax_market_status.plot(df['Date'], df['Close'].rolling(window=20).mean(), label='20-Day MA', color='green')
# ax_market_status.plot(df['Date'], df['Close'].rolling(window=50).mean(), label='50-Day MA', color='red')
# ax_market_status.fill_between(
#     df['Date'],
#     df['Bollinger_Low'],
#     df['Bollinger_High'],
#     color='grey',
#     alpha=0.1,
#     label='Bollinger Bands'
# )
# ax_market_status.set_title('Market Status Analysis')
# ax_market_status.set_xlabel('Date')
# ax_market_status.set_ylabel('Price')
# ax_market_status.legend()
# plt.tight_layout()
# plt.savefig(market_status_image)  # 保存图像
# plt.close(fig_market_status)
# 市场状态图表
fig_market_status, ax_market_status = plt.subplots(figsize=(10, 5))
fig_market_status.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax_market_status.set_facecolor("#f8f9fa")  # 绘图区背景颜色

# 绘制图表内容
ax_market_status.plot(df['Date'], df['Close'], label='Close Price', color='blue')
ax_market_status.plot(df['Date'], df['Close'].rolling(window=20).mean(), label='20-Day MA', color='green')
ax_market_status.plot(df['Date'], df['Close'].rolling(window=50).mean(), label='50-Day MA', color='red')
ax_market_status.fill_between(df['Date'], df['Bollinger_Low'], df['Bollinger_High'], color='grey', alpha=0.1)

ax_market_status.set_title('Market Status Analysis')
ax_market_status.set_xlabel('Date')
ax_market_status.set_ylabel('Price')
ax_market_status.legend()
plt.tight_layout()
plt.savefig(market_status_image)
plt.close(fig_market_status)

# 动态提示函数
def dynamic_loading_message(message, stop_event):
    """动态显示提示信息"""
    for spinner in itertools.cycle(['|', '/', '-', '\\']):
        if stop_event.is_set():  # 检查是否停止
            break
        sys.stdout.write(f"\r{message} {spinner}")
        sys.stdout.flush()
        time.sleep(0.2)
    sys.stdout.write("\r" + " " * len(message) + "\r")  # 清空提示行


def generate_analysis(image_path, description, loading_message):
    """调用大模型分析图像，并显示动态加载提示"""
    stop_event = threading.Event()
    loading_thread = threading.Thread(target=dynamic_loading_message, args=(loading_message, stop_event))
    loading_thread.start()
    try:
        result = analyze_image_with_model(image_path, description)
    finally:
        stop_event.set()
        loading_thread.join()
    return result

market_status_analysis = generate_analysis(market_status_image, "请对当前市场状态图像进行详细分析，说明趋势和风险", "正在调用大模型进行市场状态分析，请稍候...")

print("\n✅ 市场状态分析完成！")
# print(f"---\n{market_status_analysis}\n---")
# print(market_status_analysis)

# 保存 MACD 图像
macd_image = "macd_analysis.png"
fig_macd, ax_macd = plt.subplots(figsize=(10, 2))
ax_macd.plot(df['Date'], df['MACD'], label='MACD', color='blue')
ax_macd.axhline(0, color='grey', linestyle='--', alpha=0.5)
ax_macd.set_title('MACD')
ax_macd.set_xlabel('Date')
ax_macd.set_ylabel('MACD Value')
ax_macd.legend()
plt.tight_layout()
plt.savefig(macd_image)  # 保存 MACD 图像
plt.close(fig_macd)


macd_analysis = generate_analysis(macd_image, "请分析该 MACD 图，指出市场的多空趋势和潜在的变化信号,不讨论 MACD 的具体数值，要讨论增加降低趋势等其他因素", "正在调用大模型进行 MACD 分析，请稍候...")

print("\n✅ MACD 分析完成！")
# print(macd_analysis)

# 合并大模型的分析结果
combined_analysis = f"市场状态分析:\n{market_status_analysis}\n\nMACD 分析:\n{macd_analysis}"
#

text_frame = ttk.Frame(frame_market_summary)
text_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 文本框
# market_analysis_text_widget = tk.Text(text_frame, wrap="word", font=("Arial", 12), height=16)
# market_analysis_text_widget.grid(row=0, column=0, sticky="nsew")
market_analysis_text_widget = tk.Text(text_frame, wrap="word", font=("Arial", 12), height=16, bg="#f0f8ff", fg="#000000")
market_analysis_text_widget.grid(row=0, column=0, sticky="nsew")
# 滚动条
scrollbar_market_analysis = ttk.Scrollbar(text_frame, orient="vertical", command=market_analysis_text_widget.yview)
scrollbar_market_analysis.grid(row=0, column=1, sticky="ns")
# 关联滚动条
market_analysis_text_widget.config(yscrollcommand=scrollbar_market_analysis.set)
# 配置列权重，确保文本框可以扩展
text_frame.columnconfigure(0, weight=1)
text_frame.rowconfigure(0, weight=1)

###

# 显示大模型返回的分析内容
market_analysis_text_widget.insert("1.0", combined_analysis)
market_analysis_text_widget.config(state="disabled")  # 设置为只读

# 可视化技术指标
frame5 = ttk.LabelFrame(tab2, text="技术指标图表")
frame5.pack(fill="both", expand="yes", padx=10, pady=6)
# frame5 = tk.LabelFrame(tab2, text="技术指标图表", bg="#f0f8ff", font=("Arial", 12, "bold"))
# frame5.pack(fill="both", expand="yes", padx=10, pady=6)

fig2, ax2 = plt.subplots(figsize=(10, 4))
fig2.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax2.set_facecolor("#f8f9fa")  # 绘图区背景颜色

# 绘制收盘价与移动平均线
ax2.plot(df['Date'], df['Close'], label='Close Price', color='blue')
ax2.plot(df['Date'], df['Close'].rolling(window=20).mean(), label='20-Day MA', color='green')
ax2.plot(df['Date'], df['Close'].rolling(window=50).mean(), label='50-Day MA', color='red')

# 绘制RSI
ax3 = ax2.twinx()
ax3.plot(df['Date'], df['RSI'], label='RSI', color='purple', alpha=0.3)
ax3.axhline(70, color='grey', linestyle='--', alpha=0.5)
ax3.axhline(30, color='grey', linestyle='--', alpha=0.5)

ax2.set_title('Close Price and Moving Averages with RSI')
ax2.set_xlabel('Date')
ax2.set_ylabel('Price')
ax3.set_ylabel('RSI')

ax2.legend(loc='upper left')
ax3.legend(loc='upper right')

plt.xticks(rotation=45)
plt.tight_layout()

canvas2 = FigureCanvasTkAgg(fig2, master=frame5)
canvas2.draw()
canvas2.get_tk_widget().pack(fill="both", expand=True)

# 另一个技术指标图表：MACD
frame6 = ttk.LabelFrame(tab2, text="MACD 图表")
frame6.pack(fill="both", expand="yes", padx=10, pady=2)
# frame6 = tk.LabelFrame(tab2, text="MACD 图表", bg="#f0f8ff", font=("Arial", 12, "bold"))
# frame6.pack(fill="both", expand="yes", padx=10, pady=2)

fig3, ax4 = plt.subplots(figsize=(10, 2))
###颜色
fig3.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax4.set_facecolor("#f8f9fa")  # 绘图区背景颜色



ax4.plot(df['Date'], df['MACD'], label='MACD', color='blue')
ax4.axhline(0, color='grey', linestyle='--', alpha=0.5)
ax4.set_title('MACD')
ax4.set_xlabel('Date')
ax4.set_ylabel('MACD Value')
ax4.legend()

plt.xticks(rotation=45)
plt.tight_layout()

canvas3 = FigureCanvasTkAgg(fig3, master=frame6)
canvas3.draw()
canvas3.get_tk_widget().pack(fill="both", expand=True)

# 布林带图表
frame7 = ttk.LabelFrame(tab2, text="布林带图表")
frame7.pack(fill="both", expand="yes", padx=10, pady=10)
# frame7 = tk.LabelFrame(tab2, text="布林带图表", bg="#f0f8ff", font=("Arial", 12, "bold"))
# frame7.pack(fill="both", expand="yes", padx=10, pady=10)

fig4, ax5 = plt.subplots(figsize=(10, 4))
###颜色二
fig4.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax5.set_facecolor("#f8f9fa")  # 绘图区背景颜色
###
ax5.plot(df['Date'], df['Close'], label='Close Price', color='blue')
ax5.plot(df['Date'], df['Bollinger_High'], label='Bollinger High', color='green', linestyle='--')
ax5.plot(df['Date'], df['Bollinger_Low'], label='Bollinger Low', color='red', linestyle='--')
ax5.fill_between(df['Date'], df['Bollinger_Low'], df['Bollinger_High'], color='grey', alpha=0.1)
ax5.set_title('Bollinger Bands')
ax5.set_xlabel('Date')
ax5.set_ylabel('Price')
ax5.legend()

plt.xticks(rotation=45)
plt.tight_layout()

canvas4 = FigureCanvasTkAgg(fig4, master=frame7)
canvas4.draw()
canvas4.get_tk_widget().pack(fill="both", expand=True)

# 在 tab2 右下角添加 "下一页" 按钮
def go_to_next_tab3():
    """跳转到第三个选项卡"""
    notebook.select(tab3)

# 创建按钮并放置到 tab2 的右下角
next_button_tab2 = ttk.Button(tab2, text="预期与风险评估", command=go_to_next_tab3)
next_button_tab2.place(relx=0.95, rely=0.97, anchor="se")  # 右下角位置






##########################
# 选项卡3：预测与风险评估
##########################

# # 显示预测结果
frame1 = ttk.LabelFrame(tab3, text="预测结果")
frame1.pack(fill="both", expand="yes", padx=10, pady=10)
#
# tree = ttk.Treeview(frame1, columns=("Day", "Predicted Price"), show="headings", height=10)
# tree.heading("Day", text="Day")
# tree.heading("Predicted Price", text="Predicted Price")
# tree.column("Day", width=50, anchor='center')
# tree.column("Predicted Price", width=150, anchor='center')
#
# for day, price in enumerate(future_predictions_original, start=1):
#     tree.insert("", "end", values=(day, f"{price:.2f}"))
#
# tree.pack(fill="both", expand=True)
#
#
# # 显示风险点
# frame2 = ttk.LabelFrame(tab3, text="风险点")
# frame2.pack(fill="both", expand=False, padx=10, pady=2)  # 设置 expand=False 使高度可控
#
# if risks:
#     risk_tree = ttk.Treeview(frame2, columns=("Day", "Predicted Price"), show="headings", height=2)  # 限制显示5行
#     risk_tree.heading("Day", text="Day")
#     risk_tree.heading("Predicted Price", text="Predicted Price")
#     risk_tree.column("Day", width=50, anchor='center')
#     risk_tree.column("Predicted Price", width=150, anchor='center')
#
#     for day, price in risks:
#         risk_tree.insert("", "end", values=(day, f"{price:.2f}"))
#
#     risk_tree.pack(fill="both", expand=False)  # 设置 expand=False
# else:
#     tk.Label(frame2, text="未来 10 天无显著风险点").pack()
# 显示预测结果
# frame1 = tk.LabelFrame(tab3, text="预测结果", bg="#f0f8ff", fg="#000000", font=("Arial", 12, "bold"))
# frame1.pack(fill="both", expand="yes", padx=10, pady=10)

###
# 创建自定义样式
style = ttk.Style()

# 配置 Treeview 内容区域的背景颜色
style.configure(
    "Custom.Treeview",
    background="#f0f8ff",       # 内容区域背景颜色（浅蓝色）
    fieldbackground="#f0f8ff",  # 单元格背景颜色
    foreground="black"          # 内容字体颜色
)
style.map(
    "Custom.Treeview",
    background=[("selected", "#cce7ff")]  # 选中行背景颜色
)

# 配置 Treeview 表头（标题栏）样式，使其保持默认颜色
style.configure(
    "Custom.Treeview.Heading",
    background="white",  # 表头背景保持默认或其他颜色
    foreground="black",  # 表头字体颜色
    font=("Arial", 10, "bold")
)

frame1 = ttk.LabelFrame(tab3, text="预测结果")
frame1.pack(fill="both", expand="yes", padx=10, pady=10)
### Treeview 显示预测结果
tree = ttk.Treeview(frame1, columns=("Day", "Predicted Price"), show="headings", height=10, style="Custom.Treeview")
tree.heading("Day", text="Day", anchor="center")
tree.heading("Predicted Price", text="Predicted Price", anchor="center")
tree.column("Day", width=50, anchor="center")
tree.column("Predicted Price", width=150, anchor="center")

# 插入预测结果数据
for day, price in enumerate(future_predictions_original, start=1):
    tree.insert("", "end", values=(day, f"{price:.2f}"))

tree.pack(fill="both", expand=True, padx=5, pady=5)


### 显示风险点
frame2 = tk.LabelFrame(tab3, text="风险点", font=("Arial", 12, "bold"))
frame2.pack(fill="both", expand=False, padx=10, pady=2)  # 设置 expand=False 使高度可控

if risks:
    # 设置父容器背景颜色
    frame2.config(bg="#f0f8ff")

    # 创建 Treeview 控件（应用自定义样式）
    risk_tree = ttk.Treeview(frame2, columns=("Day", "Predicted Price"), show="headings", height=5, style="Custom.Treeview")
    risk_tree.heading("Day", text="Day", anchor="center")
    risk_tree.heading("Predicted Price", text="Predicted Price", anchor="center")
    risk_tree.column("Day", width=50, anchor="center")
    risk_tree.column("Predicted Price", width=150, anchor="center")

    # 插入风险数据
    for day, price in risks:
        risk_tree.insert("", "end", values=(day, f"{price:.2f}"))

    # 放置 Treeview，并设置内边距
    risk_tree.pack(fill="both", expand=False, padx=5, pady=5)
else:
    # 没有风险点时，显示提示标签，背景颜色与父容器一致
    tk.Label(frame2, text="未来 10 天无显著风险点", bg="#f0f8ff", fg="#000000").pack(padx=5, pady=5)




# 可视化预测结果和风险点
frame3 = ttk.LabelFrame(tab3, text="可视化")
frame3.pack(fill="both", expand="yes", padx=10, pady=10)

recent_history = df.iloc[-30:]
recent_dates = recent_history['Date']
recent_prices = recent_history['Close']
future_dates = pd.date_range(df['Date'].iloc[-1], periods=future_steps + 1, freq='B')[1:]

fig1, ax1 = plt.subplots(figsize=(10, 6))
###
fig1.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax1.set_facecolor("#f8f9fa")  # 绘图区背景颜色

ax1.plot(recent_dates, recent_prices, label='Recent Historical Prices', color='blue')
ax1.plot(future_dates, future_predictions_original, label='Future Predictions', color='orange')
ax1.axvline(x=recent_dates.iloc[-1], color='red', linestyle='--', label='Prediction Start')

for day, price in risks:
    ax1.scatter(future_dates[day - 1], price, color='red', label='Potential Risk' if day == risks[0][0] else "")

ax1.set_title('Future 10-Day Stock Price Predictions with Risk')
ax1.set_xlabel('Date')
ax1.set_ylabel('Close Price')
ax1.legend()
plt.xticks(rotation=45)
plt.tight_layout()

canvas1 = FigureCanvasTkAgg(fig1, master=frame3)
canvas1.draw()
canvas1.get_tk_widget().pack(fill="both", expand=True)

def go_to_next_tab4():
    """跳转到第三个选项卡"""
    notebook.select(tab4)

# 创建按钮并放置到 tab2 的右下角
next_button_tab2 = ttk.Button(tab3, text="未来市场分析", command=go_to_next_tab4)
next_button_tab2.place(relx=0.95, rely=0.97, anchor="se")  # 右下角位置









##########################
# 选项卡4：未来市场分析
##########################

# # 拼接最近历史数据和未来预测数据
combined_prices = np.concatenate((recent_prices.values, future_predictions_original))

# # 重新计算 RSI
combined_rsi = ta.momentum.RSIIndicator(pd.Series(combined_prices), window=14).rsi()
future_rsi = combined_rsi.iloc[-len(future_predictions_original):]  # 提取未来部分的 RSI

# # 重新计算 MACD
combined_macd = ta.trend.MACD(pd.Series(combined_prices)).macd_diff()
future_macd = combined_macd.iloc[-len(future_predictions_original):]  # 提取未来部分的 MACD

# # 重新计算布林带
future_bollinger = ta.volatility.BollingerBands(pd.Series(combined_prices), window=20, window_dev=2)
future_bollinger_high = future_bollinger.bollinger_hband().iloc[-len(future_predictions_original):]
future_bollinger_low = future_bollinger.bollinger_lband().iloc[-len(future_predictions_original):]

# 保存 RSI 图像
rsi_image = "future_rsi_analysis.png"
fig_rsi, ax_rsi = plt.subplots(figsize=(10, 4))
###
fig_rsi.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
ax_rsi.set_facecolor("#f8f9fa")  # 绘图区背景颜色

ax_rsi.plot(future_dates, future_rsi, label='RSI', color='purple')
ax_rsi.axhline(70, color='grey', linestyle='--', alpha=0.5, label='Overbought (70)')
ax_rsi.axhline(30, color='grey', linestyle='--', alpha=0.5, label='Oversold (30)')
ax_rsi.set_title('Future Trends in RSI Indicators')
ax_rsi.set_xlabel('Date')
ax_rsi.set_ylabel('RSI Value')
ax_rsi.legend()
plt.tight_layout()
plt.savefig(rsi_image)
plt.close(fig_rsi)

# 保存 MACD 图像
macd_image = "future_macd_analysis.png"
fig_macd, ax_macd = plt.subplots(figsize=(10, 4))
ax_macd.plot(future_dates, future_macd, label='MACD', color='blue')
ax_macd.axhline(0, color='grey', linestyle='--', alpha=0.5, label='Neutral (0)')
ax_macd.set_title('Future MACD Indicator Trends')
ax_macd.set_xlabel('Date')
ax_macd.set_ylabel('MACD Value')
ax_macd.legend()
plt.tight_layout()
plt.savefig(macd_image)
plt.close(fig_macd)

# 保存布林带图像
bollinger_image = "future_bollinger_analysis.png"
fig_bollinger, ax_bollinger = plt.subplots(figsize=(10, 4))
ax_bollinger.plot(future_dates, future_predictions_original, label='Closing Price', color='blue')
ax_bollinger.plot(future_dates, future_bollinger_high, label='Upper Bollinger Band', color='green', linestyle='--')
ax_bollinger.plot(future_dates, future_bollinger_low, label='Lower Bollinger Band', color='red', linestyle='--')
ax_bollinger.fill_between(future_dates, future_bollinger_low, future_bollinger_high, color='grey', alpha=0.1)
ax_bollinger.set_title('Future Trends in Bollinger Bands')
ax_bollinger.set_xlabel('Date')
ax_bollinger.set_ylabel('Price')
ax_bollinger.legend()
plt.tight_layout()
plt.savefig(bollinger_image)
plt.close(fig_bollinger)

rsi_analysis = generate_analysis(rsi_image, "请分析未来 RSI 图，说明市场是否存在超买或超卖信号，并预测未来趋势", "正在调用大模型进行 RSI 分析，请稍候...")
macd_analysis = generate_analysis(macd_image, "请分析未来 MACD 图，指出市场的多空趋势和潜在变化信号", "正在调用大模型进行 MACD 分析，请稍候...")
bollinger_analysis = generate_analysis(bollinger_image, "请分析未来布林带图，说明市场波动性及可能的价格趋势", "正在调用大模型进行 布林带图 分析，请稍候...")

# 打印完成提示
print("\n✅ RSI，MACD，布林带图分析完成！")



# 合并分析结果
future_trends_analysis = f"""
未来市场分析：

1. **RSI 图分析**：
{rsi_analysis}

2. **MACD 图分析**：
{macd_analysis}

3. **布林带图分析**：
{bollinger_analysis}
"""

#####

# 添加大模型分析结果显示框
frame_future_summary = ttk.LabelFrame(tab4, text="大模型未来市场分析")
frame_future_summary.pack(fill="both", expand="yes", padx=10, pady=5)
# 使用 Frame 包裹 Text 和 Scrollbar
text_frame = ttk.Frame(frame_future_summary)
text_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 文本框
# future_analysis_text_widget = tk.Text(text_frame, wrap="word", font=("Arial", 12), height=10)
# future_analysis_text_widget.grid(row=0, column=0, sticky="nsew")
future_analysis_text_widget = tk.Text(text_frame, wrap="word", font=("Arial", 12), height=16, bg="#f0f8ff", fg="#000000")
future_analysis_text_widget.grid(row=0, column=0, sticky="nsew")
# 滚动条
scrollbar_future_analysis = ttk.Scrollbar(text_frame, orient="vertical", command=future_analysis_text_widget.yview)
scrollbar_future_analysis.grid(row=0, column=1, sticky="ns")
# 关联滚动条
future_analysis_text_widget.config(yscrollcommand=scrollbar_future_analysis.set)
# 配置列权重，确保文本框可以自动扩展
text_frame.columnconfigure(0, weight=1)
text_frame.rowconfigure(0, weight=1)
# 写入分析内容并设置为只读
future_analysis_text_widget.insert("1.0", future_trends_analysis)
future_analysis_text_widget.config(state="disabled") # 设置为只读


# 图表切换界面
frame_future_charts = ttk.LabelFrame(tab4, text="未来预测技术指标图表")
frame_future_charts.pack(fill="both", expand="yes", padx=10, pady=10)

# 当前显示的图表索引
current_chart = [0]  # 用列表包裹，使得闭包中可以修改

def plot_chart(chart_index):
    """根据索引绘制相应的图表"""
    fig, ax = plt.subplots(figsize=(12, 3))  # 创建单个子图
    ###
    fig.set_facecolor("#f0f8ff")  # 整个图表的背景颜色
    ax.set_facecolor("#f8f9fa")  # 绘图区背景颜色
    fig.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.2)  # 调整子图边距
    # ax.clear()

    if chart_index == 0:  # RSI 图
        ax.plot(future_dates, future_rsi, label='RSI', color='purple')
        ax.axhline(70, color='grey', linestyle='--', alpha=0.5, label='Overbought (70)')
        ax.axhline(30, color='grey', linestyle='--', alpha=0.5, label='Oversold (30)')
        ax.set_title('1. Future Trends in RSI Indicators', fontsize=14, fontweight='bold', color='darkblue')
        ax.set_xlabel('Date', fontsize=12, fontweight='bold', color='darkgreen')
        ax.set_ylabel('RSI Value', fontsize=12, fontweight='bold', color='darkgreen')
        ax.legend(loc='upper right', fontsize=10, frameon=True, shadow=True)

    elif chart_index == 1:  # MACD 图
        ax.plot(future_dates, future_macd, label='MACD', color='blue')
        ax.axhline(0, color='grey', linestyle='--', alpha=0.5, label='Neutral (0)')
        ax.set_title('2. Future MACD Indicator Trends', fontsize=14, fontweight='bold', color='darkblue')
        ax.set_xlabel('Date', fontsize=12, fontweight='bold', color='darkgreen')
        ax.set_ylabel('MACD Value', fontsize=12, fontweight='bold', color='darkgreen')
        ax.legend(loc='upper right', fontsize=10, frameon=True, shadow=True)

    elif chart_index == 2:  # 布林带图
        ax.plot(future_dates, future_predictions_original, label='Closing Price', color='blue')
        ax.plot(future_dates, future_bollinger_high, label='Upper Bollinger Band', color='green', linestyle='--')
        ax.plot(future_dates, future_bollinger_low, label='Lower Bollinger Band', color='red', linestyle='--')
        ax.fill_between(future_dates, future_bollinger_low, future_bollinger_high, color='grey', alpha=0.1)
        ax.set_title('3. Future Trends in Bollinger Bands', fontsize=14, fontweight='bold', color='darkblue')
        ax.set_xlabel('Date', fontsize=12, fontweight='bold', color='darkgreen')
        ax.set_ylabel('Price', fontsize=12, fontweight='bold', color='darkgreen')
        ax.legend(loc='upper right', fontsize=10, frameon=True, shadow=True)

    return fig

# 初始化显示第一个图表
canvas_future = FigureCanvasTkAgg(plot_chart(current_chart[0]), master=frame_future_charts)
canvas_widget = canvas_future.get_tk_widget()

# 使用 place 确保图像居中
canvas_widget.place(relx=0.5, rely=0.5, anchor="center")  # 使用 relx 和 rely 确保居中
canvas_future.draw()

def update_chart():
    """更新当前图表显示"""
    global canvas_future, canvas_widget
    canvas_widget.place_forget()  # 移除当前图表
    canvas_future.figure = plot_chart(current_chart[0])
    canvas_future.draw()
    canvas_widget = canvas_future.get_tk_widget()
    canvas_widget.place(relx=0.5, rely=0.5, anchor="center")  # 重新居中
def next_chart():
    """切换到下一个图表"""
    current_chart[0] = (current_chart[0] + 1) % 3  # 循环切换（0, 1, 2）
    canvas_future.figure = plot_chart(current_chart[0])
    canvas_future.draw()

# 添加切换按钮
button_frame = ttk.Frame(frame_future_charts)
button_frame.pack(side="bottom", fill="x", padx=10, pady=5)
next_button = ttk.Button(button_frame, text="Next Chart", command=next_chart)
next_button.pack(side="right")






# 运行 Tkinter 主循环
root.mainloop()





